package Control;

import java.sql.ResultSet;

import objects.Chromosome;
import objects.Exon;
import objects.Gene;
import objects.Subset;
import objects.Transcript;
import objects.Treatment;
import objects.alignments.DefaultAlignment;
import objects.alignments.IntronAlignment;
import objects.alignments.SpliceAlignment;
import objects.alignments.Subset0Alignment;


/**
 * Loads all of the data from the database into the models. This will be the
 * only class that actually holds SQL commands to integrate into the database.
 * 
 * Precondition: When InitializeData is called, the data needs to be loaded.
 * This means that inside the calling class their should be a way to make sure
 * that the current gene isn't loaded again (it takes to long to load the data
 * for the gene).
 * 
 * @author stephenbwilliamson
 */
public class InitializeData {

	private static InitializeData instance = null;
	protected static DataConnection conn;

	protected InitializeData() {
	}

	public static InitializeData getInstance() {
		if (instance == null) {
			instance = new InitializeData();
		}
		return instance;
	}

	private void connect() {
		if (conn == null) {
			conn = DataConnection.getInstance();
		}
	}

	/**
	 * Fetches the entire gene list from the database.
	 */

	public String[] getGeneList() {
		String[] list = null;

		try {
			ResultSet rs = DataConnection.getInstance().executeQuery(
					"SELECT count(*) FROM GeneModels WHERE type=\"gene\"");
			if (rs.next()) {
				list = new String[rs.getInt("count(*)") + 1];
			} else {
				list = new String[1];
			}

			list[0] = "--- none ---";
			rs = DataConnection.getInstance().executeQuery(
					"SELECT Feature_ID FROM GeneModels WHERE type=\"gene\"");
			for (int i = 1; rs.next(); i++) {
				list[i] = rs.getString("Feature_ID");
			}
		} catch (Exception e) {
			handleException(e);
		}

		return list;
	}

	/**
	 * Given a gene name, the gene is returned (from the database).
	 */
	public Gene loadGene(String geneName) throws Exception {
		connect();
		ResultSet rs = conn
				.executeQuery("SELECT * FROM GeneModels where Feature_ID='"
						+ geneName + "'");
		if (!rs.next()) {
			throw new Exception("Could not find gene '" + geneName + "'.");
		}

		String name = rs.getString("Feature_ID");
		String source = rs.getString("Source");
		String itemType = rs.getString("GFF_ItemType");
		int start = rs.getInt("Start");
		int end = rs.getInt("End");
		char strand = (char) rs.getBytes("Strand")[0];
		String parentChromosome = rs.getString("Parent");

		Gene gene = new Gene(name, source, itemType, start, end, strand,
				parentChromosome);
		Controller.getInstance().addGene(gene);

		return gene;
	}

	/**
	 * Given the gene, the chromosome data is loaded. This isn't used in a
	 * current configuration, but may be necessary at a later date.
	 * 
	 * @precondition The gene contains a valid Chromosome as a parent. This is
	 *               satisfied if the gene was loaded from [code]loadGene[/code]
	 */
	public void loadChromosome(Gene gene) throws Exception {
		connect();
		Chromosome chr = gene.getParentChromosome();
		String featureID = chr.getName();

		System.out.println("Feature ID: " + featureID);
		conn.closeConnection();
		ResultSet rs = conn
				.executeQuery("SELECT * FROM GeneModels WHERE `Type`='chromosome' AND Feature_ID = '"
						+ featureID + "'");
		if (!rs.next()) {
			return;
		}

		String source = rs.getString("Source");
		int start = rs.getInt("Start");
		int end = rs.getInt("End");
		chr.setSource(source);
		chr.setBounds(start, end);

		Controller.getInstance().addChromosome(chr);
	}

	/**
	 * Given the gene, loads the corresponding transcripts (isoforms) of the
	 * gene.
	 */
	public void loadTranscripts(Gene gene) throws Exception {
		connect();
		String name = gene.getName();

		ResultSet rs = conn
				.executeQuery("SELECT * FROM GeneModels WHERE `Type`='transcript' AND Parent='"
						+ name + "'");

		while (rs.next()) {
			String transcriptName = rs.getString("Feature_ID");
			String source = rs.getString("Source");
			String type = rs.getString("GFF_ItemType");
			int start = rs.getInt("Start");
			int end = rs.getInt("End");

			Transcript t = new Transcript(transcriptName, source, type, start,
					end);
			t.setGeneParent(gene);
			gene.addTranscript(t);
		}
	}

	/**
	 * Loads each exon based on the corresponding transcript.
	 */
	public void loadExons(Transcript t) throws Exception {
		connect();
		String name = t.getName();
		ResultSet rs = conn
				.executeQuery("SELECT * FROM GeneModels where Type='exon' and Parent ='"
						+ name + "' ORDER BY Start");

		while (rs.next()) {
			String exonName = rs.getString("Feature_ID");
			String source = rs.getString("Source");
			String type = rs.getString("GFF_ItemType");
			int start = rs.getInt("Start");
			int end = rs.getInt("End");

			Exon ex = new Exon(exonName, source, type, start, end);
			ex.setParent(t);
			t.addExon(ex);
		}
	}

	/**
	 * Adds all Treatment data to the gene.
	 */
	public void loadTreatments(Gene gene) throws Exception {
		connect();
		String name = gene.getName();

		ResultSet rs = conn
				.executeQuery("SELECT Sample_ID FROM Alignments WHERE Gene='"
						+ name + "' GROUP BY Sample_ID");
		while (rs.next()) {
			int sampleID = rs.getInt("Sample_ID");
			Treatment t = new Treatment(sampleID);
			t.setGeneParent(gene);
			gene.addTreatment(t);
		}
	}

	public void loadSubsets(Treatment t) throws Exception {
		connect();
		String base = t.getGeneParent().getName();

		loadSubset0(t);

		ResultSet rs = conn
				.executeQuery("SELECT Subset FROM DesignMatrices_Unpaired WHERE Transcript LIKE '"
						+ base + ".%' GROUP BY Subset");
		while (rs.next()) {
			int subset = rs.getInt("Subset");
			Subset sub = new Subset(subset);
			sub.setTreatmentParent(t);
			t.addSubset(sub);
		}
	}

	public void loadAlignments(Subset sub) throws Exception {
		connect();
		String geneName = sub.getTreatmentParent().getGeneParent().getName();
		int treatmentID = sub.getTreatmentParent().getTreatmentID();
		int subsetID = sub.getSubsetID();

		String sql = "SELECT Start_Pos_Gene, count(*) FROM DesignMatrices_Unpaired, "
				+ "Alignments WHERE Gene = '"
				+ geneName
				+ "' AND Subset = "
				+ subsetID
				+ " AND Sample_ID = "
				+ treatmentID
				+ " AND Alignments.Transcript = "
				+ "DesignMatrices_Unpaired.Transcript AND Start_Pos_Gene = ReadStartPos_Gene "
				+ "GROUP BY Start_Pos_Gene ORDER BY ReadStartPos_Gene";
		// System.out.println ("SQL: " + sql);
		// String sql = "SELECT Alignment_ID, "
		// +
		// "TargetFeature_ID, Target_Type, Start_Pos_Gene, Alignments.Transcript, "
		// + "CigarString FROM Alignments, DesignMatrices_Unpaired WHERE "
		// + "Gene ='" + geneName + "' and Sample_ID = " + treatmentID +
		// " AND Subset = " + subsetID + " AND MultiGene_Flag = 0 "
		// +
		// "AND DesignMatrices_Unpaired.`ReadStartPos_Gene` = Alignments.`Start_Pos_Gene` "
		// +
		// "AND DesignMatrices_Unpaired.`Transcript` = Alignments.Transcript AND "
		// +
		// "Start_Pos_Transcript = ReadStartPos GROUP BY Read_ID ORDER BY Start_Pos_Gene";
		ResultSet rs = conn.executeQuery(sql);

		while (rs.next()) {
			int start = rs.getInt("Start_Pos_Gene");
			int count = rs.getInt("count(*)");

			DefaultAlignment a = new DefaultAlignment(start, count, sub);
			sub.addAlignment(a);
		}
	}

	private void loadSubset0(Treatment t) throws Exception {
		connect();
		String gene = t.getGeneParent().getName();
		int treatmentID = t.getTreatmentID();

		Subset sub = new Subset(0);

		ResultSet rs = conn
				.executeQuery("SELECT Start_Pos_Gene, CigarString FROM Alignments WHERE sample_id = "
						+ treatmentID
						+ " and gene = '"
						+ gene
						+ "' and target_type = 'gene' and MultiGene_Flag = 0");
		while (rs.next()) {
			int start = rs.getInt("Start_Pos_Gene");
			String cigarString = rs.getString("CigarString");

			Subset0Alignment a = null;
			if (cigarString.contains("N"))
				a = new SpliceAlignment(start, sub, cigarString);
			else
				a = new IntronAlignment(start, sub, cigarString);

			sub.addAlignment(a);
		}
		t.addSubset(sub);
	}

	public void handleException(Exception e) {
		if (e != null) {
			System.err.println("ERROR: " + e.getMessage());
			e.printStackTrace();
		}
	}
}
